package com.haibo.utils;

/**
 * @Author : 刘海波
 * @Date: 2025/8/26 16:40
 * @Description:
 * 雪花算法ID生成器
 * 生成64位Long类型ID，结构如下：
 * 1位符号位（固定0） + 41位时间戳 + 10位机器ID + 12位序列号
 */
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SnowflakeIdGeneratorUtil {

    // 开始时间戳 (2020-01-01 00:00:00)
    private final long startTime = 1577808000000L;

    // 机器ID所占的位数
    private final long workerIdBits = 10L;

    // 支持的最大机器ID，结果是1023 (这个移位算法可以很快计算出几位二进制数所能表示的最大十进制数)
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    // 序列在ID中占的位数
    private final long sequenceBits = 12L;

    // 机器ID向左移12位
    private final long workerIdShift = sequenceBits;

    // 时间戳向左移22位(10+12)
    private final long timestampLeftShift = sequenceBits + workerIdBits;

    // 生成序列的掩码，这里为4095 (0b111111111111=4095)
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    // 机器ID
    private final long workerId;

    // 毫秒内序列(0~4095)
    private long sequence = 0L;

    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;

    // 锁对象，确保多线程环境下的线程安全
    private final Lock lock = new ReentrantLock();

    // 单例缓存
    private static final ConcurrentMap<Long, SnowflakeIdGeneratorUtil> INSTANCES = new ConcurrentHashMap<>();

    /**
     * 私有构造函数，防止外部实例化
     * @param workerId 机器ID
     */
    private SnowflakeIdGeneratorUtil(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId不能大于%d或小于0", maxWorkerId));
        }
        this.workerId = workerId;
    }

    /**
     * 获取单例实例
     * @param workerId 机器ID
     * @return 雪花算法生成器实例
     */
    public static SnowflakeIdGeneratorUtil getInstance(long workerId) {
        return INSTANCES.computeIfAbsent(workerId, k -> new SnowflakeIdGeneratorUtil(workerId));
    }

    /**
     * 生成下一个ID
     * @return 生成的ID
     */
    public long nextId() {
        lock.lock();
        try {
            long timestamp = System.currentTimeMillis();

            // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过，这时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("系统时钟回退，拒绝生成ID。时间差为: %d 毫秒", lastTimestamp - timestamp));
            }

            // 如果是同一时间生成的，则进行毫秒内序列自增
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                // 毫秒内序列溢出
                if (sequence == 0) {
                    // 阻塞到下一个毫秒，获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                // 时间戳改变，毫秒内序列重置
                sequence = 0L;
            }

            // 更新上次生成ID的时间戳
            lastTimestamp = timestamp;

            // 移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - startTime) << timestampLeftShift) // 时间戳部分
                    | (workerId << workerIdShift) // 机器ID部分
                    | sequence; // 序列号部分
        } finally {
            lock.unlock();
        }
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 当前时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    // 测试方法
    public static void main(String[] args) {
        // 使用机器ID 1创建生成器实例
        SnowflakeIdGeneratorUtil idGenerator = SnowflakeIdGeneratorUtil.getInstance(1);

        // 生成10个ID并打印
        for (int i = 0; i < 10; i++) {
            long id = idGenerator.nextId();
            System.out.println("生成的ID: " + id + ", 长度: " + String.valueOf(id).length());
        }
    }
}

